
// patching proper
var currentDownloadingProductName='Unknown';
var lastDownloadingProductName='Unknown';
var patchCompleteCallback = null;

// for determining progress stalling
var lastByteDate = null;
var lastByteChecker = 0;

function httpTargetCallback(url)
{
    var x;
    if((manifesturl)&&(manifesturl!=external_manifest_url))
    {
    	x=url.indexOf(external_manifest_url);
    	if(x>=0)
    		url=url.substr(0,x)+manifesturl+url.substr(x+external_manifest_url.length);
    }
    if((user != null) 
    && (user != undefined) 
    && (user.patch_token != null) 
    && (user.patch_token != undefined) 
    && (user.patch_token != '')
    && ((x=user.patch_token.indexOf('_'))>0))
    {
        var partOne=user.patch_token.substr(0,x);
        var partTwo=user.patch_token.substr(x+1);
        return "{Header:Cookie,md5=access=/*~expires="+partOne+"~md5="+partTwo+"; }" + url;
    }
    else
        return url;
}

function patchErrorHandler(errorCode, time, message)
{
	log('ERROR','Recorded error (b4='+SSN.isErrorMessageOpen+': '+errorCode+": "+message);
    SSN.isErrorMessageOpen = true;
   	if( $("#pchecking").is(":visible") )
		screen.pchecking.hide();

    if (errorCode == SSN.PATCHER_ERROR_DRM_CONTACT || errorCode == SSN.PATCHER_ERROR_RETRIEVEPACKAGE || errorCode == SSN.PATCHER_ERROR_DOWNLOAD) {
        error(strings[lang]["Unable to retrieve patch data."]).show();
        disableButton();
    }
    else if (errorCode == SSN.PATCHER_ERROR_COPY || errorCode == SSN.PATCHER_ERROR_DATAFORMAT || errorCode == SSN.PATCHER_ERROR_COPY_DIFFSOURCEVERIFY) {
        error(strings[lang]["Error writing patch."]).show();
        disableButton();
        abortCurrentPatch();
    }
    else if (errorCode == SSN.PATCHER_ERROR_DRM_AUTH || errorCode == SSN.PATCHER_ERROR_DRM_PERMISSION) {
        error(strings[lang]["Error authorizing patch."]).show();
        disableButton();
    }
    else if (errorCode == SSN.PATCHER_ERROR_DISKSPACE) {
        error(strings[lang]["Error insufficient disk space."]).show();
        disableButton();
        abortCurrentPatch();
    }
    else if (errorCode == SSN.LAUNCHERERRORTYPE_WARN_ELEVATIONREQUEST) {
        // the os already warns, so this is redundant
    }
    else {
        error(strings[lang]["Error restart required."]).show()
        disableButton();
    }
}


function packageAllowRestartCallback(packageName)
{
    if (restartTimeoutStarted == false)
    {
    	log('INFO','Launcher restart is happening...');
        restartTimeoutStarted = true;
        var delayRestart = SSN.Launcher.getConfig('RestartDelaySeconds', '2') * 1000;
        setTimeout('allowRestart = true;', delayRestart);
        if((user != null) && (user != undefined))
        {
	        SSN.Launcher.setSetting('RestartGameKey', user.token);
	        SSN.Launcher.setSetting('RestartPatchKey', user.patch_token);
        }
        else
        {
	        SSN.Launcher.setSetting('RestartGameKey', '');
	        SSN.Launcher.setSetting('RestartPatchKey', '');
        }
        if((pickedEnv != null) && (pickedEnv != undefined))
        {
            SSN.Launcher.setSetting('RestartEnvironment', pickedEnv.name);
            SSN.Launcher.setSetting('RestartProduct', pickedProduct);
        }
        SSN.Launcher.saveSettings();
        $("#ppatching").hide();
        screen.restarting.show();
    }
    return allowRestart;
}

function changeLangPackageList(oldLang)
{
	if((currentDownloadingProductName==null) 
	||(currentDownloadingProductName=='')
	||(currentDownloadingProductName=='Unknown')
	||(oldLang==null)
    ||(oldLang==lang))
		return false;
	var x=currentDownloadingProductName.indexOf('_'+oldLang.replace('-','_'));
	if(x>=0)
	{
		SSN.Patcher.removePackageList(currentDownloadingProductName);
		currentDownloadingProductName = currentDownloadingProductName.substr(0,x)+'_'+lang.replace('-','_');
		log('INFO','Linguistic package changed; starting patch of product '+currentDownloadingProductName);
    screen.patching.showProgress();
    SSNStartDownload();
    return true;
	}
	return false;
}

function tempRetryPackageList()
{
	if(currentDownloadingProductName==null) return false;
	var x=currentDownloadingProductName.indexOf('_'+lang.replace('-','_'));
	if(x>=0)
	{
		SSN.Patcher.removePackageList(currentDownloadingProductName);
		currentDownloadingProductName = currentDownloadingProductName.substr(0,x);
		log('INFO','Linguistic package not found; starting patch of product '+currentDownloadingProductName);
	    SSNStartDownload();
	    return true;
	}
	return false;
}

function round2(num){
  num = ""+Math.round(num*100)/100;
  var i = num.indexOf('.');
  if(i<0){ return num+".00"; }
  else{ return num + ".00".substr(num.length-i); }
}

var lastPercent=0;
function packageListStatusCallback(packageListName, packageName, packageListState,
								   patcherState, currentName, currentVersion, targetName,
							       targetVersion, targetDescriptorType,
							       packageType, active, totalBytes, downloadBytesLeft,
							       scanBytesLeft, copyBytesTotal, copyBytesLeft,
							       maxIncoming, averageIncoming, currentIncoming,
							       maxOutgoing, averageOutgoing, currentOutgoing,
							       bytesRead, bytesSent)
{
	if((patcherState == SSN.PATCHER_STATE_FAILED) || (patcherState == SSN.PATCHER_STATE_INVALID))
	{
		patchErrorHandler(SSN.PATCHER_ERROR_DOWNLOAD, new Date().getTime(), 'bad patcher state');
		//return;
	}
	if((packageListState == SSN.PACKAGELIST_STATE_INVALID) || (packageListState == SSN.PACKAGELIST_STATE_FAILED))
	{
		if(!tempRetryPackageList())
			patchErrorHandler(SSN.PATCHER_ERROR_DOWNLOAD, new Date().getTime(),'bad package list state');
		return;
	}
	
	var displayablePatchableName='Downloading';
	packageName=packageName.toLowerCase();
	var versionString = (packageType==2) ? (currentVersion+2) : '' ;
	if(packageName.indexOf('playerclient')==0)
		displayablePatchableName='Player Client '+versionString;
	else
	if(packageName.indexOf('heroblade')==0)
		displayablePatchableName='HeroBlade '+versionString;
	else
	if(packageName.indexOf('retailclient')==0)
		displayablePatchableName='Game Client '+versionString;
	else
	if(packageName.indexOf('repositoryassistant')==0)
		displayablePatchableName='Repo Asst. '+versionString;
	else
	if(packageName.indexOf('repositorybrowser')==0)
		displayablePatchableName='Repo Browser '+versionString;
	else
	if(packageName.indexOf('visualizer')==0)
		displayablePatchableName='Visualizer '+versionString;
	else
	if(packageName.indexOf('morpheme')==0)
		displayablePatchableName='Morpheme '+versionString;
	else
	if(packageName.indexOf('biomon')==0)
		displayablePatchableName='Biomon '+versionString;
	else
	if(packageName.indexOf('byftools')==0)
		displayablePatchableName='BYF Pen '+versionString;
	else
	if(packageName.indexOf('assets')>=0)
	{
		if(packageName.indexOf('main')>=0)
			displayablePatchableName='Main Assets '+versionString;
		else
		if(packageName.indexOf('en_us')>=0)
			displayablePatchableName='English Assets '+versionString;
		else
		if(packageName.indexOf('fr_fr')>=0)
			displayablePatchableName='French Assets '+versionString;
		else
		if(packageName.indexOf('de_de')>=0)
			displayablePatchableName='German Assets '+versionString;
	}
	else
	if(packageName.indexOf('movies')>=0)
	{
		if(packageName.indexOf('en_us')>=0)
			displayablePatchableName='English Video '+versionString;
		else
		if(packageName.indexOf('fr_fr')>=0)
			displayablePatchableName='French Video '+versionString;
		else
		if(packageName.indexOf('de_de')>=0)
			displayablePatchableName='German Video '+versionString;
	}
	
    $(".dispProdName").html(displayablePatchableName);
	
	if((packageListName == "patcher") && (currentDownloadingProductName == "patcher"))
	{
	    if((downloadBytesLeft > 0) || (copyBytesLeft > 0))
	    {
	    	if( !$("#ppatching").is(":visible") )
	    	{
	            $("#pchecking").hide();
		        screen.ppatching.show();
	    	}
			// we are updating the patcher, this is a special case.
	    }
		return;
	}
    if((downloadBytesLeft > 0)&&(packageListState!=SSN.PACKAGELIST_STATE_UPDATE_COMPLETE))
    {
    	if((downloadBytesLeft > lastByteChecker)||(lastByteChecker==0))
    	{
        	lastByteChecker = downloadBytesLeft;
    	}
    	
    	if(((lastByteChecker - downloadBytesLeft) > (4096))||(SSN.isPaused))
    	{
    		lastByteChecker = downloadBytesLeft;
    		lastByteDate = new Date();
    	    SSN.isErrorMessageOpen = false;
    		error.hide();
    	}
    	else
    	if((lastByteDate != null)&&(lastByteDate != undefined)&&(!SSN.isErrorMessageOpen))
    	{
	        var rightNow = new Date();
	        var difference = rightNow - lastByteDate;
	        var secondsDifference = Math.round(difference/(1000));
	        if((secondsDifference >= 90) && ((secondsDifference % 90)==0))
	        {
	        	var msg = strings[lang]['Slow download detected: <BYTES> last <TIME> seconds'];
	        	msg = msg.replace('<BYTES>',''+(downloadBytesLeft - lastByteChecker));
	        	msg = msg.replace('<TIME>',''+secondsDifference);
	        	error.warn(msg).show();
	        }
    	}
    	
        $("#installing").hide();
        $("#installPercent").text('');
        $("#doingNothing").hide();
        $("#verifying").hide();
        $("#verifyPercent").text('');
        $("#downloading").show();
        var percent = (totalBytes-downloadBytesLeft)/totalBytes; //maybe we could use bytesRead instead, but this seemed like a solid choice
        if(percent<0){ percent=0; }
        if(percent>1){ percent=1; }
        var rate = averageIncoming || currentIncoming; //maybe currentIncoming is more interesting to see than the average, we should test this.
        if( downloadBytesLeft/1e9 > 1.5 ){
          $("#downloadRemaining").text( round2(downloadBytesLeft/1e9) + "GB" );
        }else{
          $("#downloadRemaining").text( round2(downloadBytesLeft/1e6) + "MB" );
        }
        if(rate && rate > 0.01){ //my guess is that averageIncoming is measured in bytes/second
          if(round2(rate/1e6)==0.0)
              $("#downloadSpeed").text( ' ' );
          else
          if( rate/1e6 > 1.5 ){
            $("#downloadSpeed").text( ' (' + round2(rate/1e6) + "MB/s)" );
          }else{
            $("#downloadSpeed").text( ' (' + round2(rate/1e3) + "kB/s)" );
          }
        }
        $("#progressPercent").text( Math.floor(percent*100) + "%" ); //flooring is important so 100% means it's actually finished
        if( lastPercent>percent ){ $("#progressbarFilled").stop().width("0%"); }
        $("#progressbarFilled").stop().animate({width:(percent*100)+"%"},1000,"linear");
        lastPercent = percent;
    }
    else
    if((copyBytesLeft > 0)&&(packageListState!=SSN.PACKAGELIST_STATE_UPDATE_COMPLETE))
    {
        lastByteChecker = 0;
        percent = (copyBytesTotal-copyBytesLeft)/copyBytesTotal; //maybe we could use bytesRead instead, but this seemed like a solid choice
        if(percent<0){ percent=0; }
        if(percent>1){ percent=1; }
        
        $("#doingNothing").hide();
        $("#downloading").hide();
        if(patcherState==SSN.PATCHER_STATE_COPY)
        {
            $("#verifying").hide();
            $("#verifyPercent").text('');
            $("#installing").show();
            $("#installPercent").text( ' (' + Math.floor(percent*100) + "%)" ); //flooring is important so 100% means it's actually finished
        }
        else
        {
            $("#installing").hide();
            $("#installPercent").text('');
            $("#verifying").show();
            $("#verifyPercent").text( ' (' + Math.floor(percent*100) + "%)" ); //flooring is important so 100% means it's actually finished
        }
        if( lastPercent>percent ){ $("#progressbarFilled").stop().width("0%"); }
        $("#progressbarFilled").stop().animate({width:(percent*100)+"%"},1000,"linear");
        lastPercent = percent;
    }
    else
    {
		lastByteChecker = 0;
        // all this works whether we are patching the patcher or something else
        if (SSN.canLaunch(packageListState, targetDescriptorType, patcherState) == true) 
        {
           log('INFO',"Patch completed normally");
           $("#progressbarFilled").stop().animate({width:"100%"},300,"linear");
           lastPercent=1;
           setTimeout(patchCompleteCallBack,300);
        } 
        else 
        {
            //log('DEBUG',"Patch completed in a wait state!");
            $("#progressbarFilled").stop().width( "0%" );
            lastPercent=0;
        }
    }
}

function startPatch(environment, productName)
{
    pickedEnv = environment;
    pickedProduct = productName;
    if(!SatisfyGameRequirements())
    {
        return;
    }
    log('DEBUG','StartPatch called -- requirements satisfied.');
    if((pickedEnv != null)&&(pickedEnv!=undefined))
    {
        var autoEnv = (SSN.Launcher.getSetting('EnableAutoEnvironment', 'false').toLowerCase() == 'true');
        var lastEnv = SSN.Launcher.getSetting('LastEnvironment');
        if((autoEnv)&&(autoEnv!=undefined)&&(autoEnv!='undefined'))
        {
            SSN.Launcher.setSetting('LastEnvironment', pickedEnv.name)
            SSN.Launcher.setSetting('LastProduct', pickedProduct)
            SSN.Launcher.saveSettings();
        }
        else
        if((lastEnv!=null)&&(lastEnv!=undefined)&&(lastEnv!='undefined')&&(lastEnv.length>0))
        {
            SSN.Launcher.setSetting('EnableAutoEnvironment', 'false')
            SSN.Launcher.setSetting('LastEnvironment', '')
            SSN.Launcher.setSetting('LastProduct', '')
            SSN.Launcher.saveSettings();
        }
    }
    var product = 'retailclient'+'_'+'he603';
		
    patchCompleteCallBack=function()
    {
       if(autoCloseStr == "CLOSE")
       {
           CloseLauncher();
           return;
       }
       else
       if(autoCloseStr == "PLAY")
       {
           setTimeout(function(){ launchProgram(); },500);
           return;
       }
       FinishedPatchingUI();
    }
    currentDownloadingProductName = product+'_'+lang.replace('-','_');
    if((pickedEnv.altManifestUrl) && (pickedEnv.altManifestUrl != '') && (pickedEnv.altManifestUrl != 'null')) {
		}
    else {
			//manifesturl=external_manifest_url;
			manifesturl="manifest.swtor.com";
			log('INFO','Starting patch of product '+product);
			setTimeout(SSNStartDownload,1);
			//$("#updateButton, #pauseButton, #resumeButton").hide();
			//screen.promos.show();
		}
}

function FinishedPatchingUI()
{
    SSN.Patcher.removePackageList(currentDownloadingProductName);
    $("#updateButton, #pauseButton, #resumeButton").hide();
    $("#nextButton").show().siblings(".on").stop().animate({opacity:0.7},200,'ease'); //glow next button
    screen.patching.hideProgress();
    setTimeout(function(){
      $("#installing, #downloading, #verifying").hide();
      $("#doingNothing").show();
      $("#progressbarFilled").stop().width("0%");
    },400);
    //if((!pickedEnv.isOnline) || (pickedEnv.patchOnly))
		if (2 == 1)
    {
        error.show();
        error(strings[lang]["This environment is currently unavailable for play.  Please try again later."]).show();
        log('INFO','Patch-Only state reached.  User must exit.');
        disableButton();
    }
    else
    {
        //setTimeout(StartLastDownload,30 * 60 * 1000);
				/*
        for(var key in screen) {
           //log('INFO', '\nkey: ' + key + '\n' + 'value: ' + screen[key] + '\n');
					 if (key == 'environments') {
						log('INFO', 'Envi key = ' +key);
						for(var key2 in screen[key]) {
							log('INFO', '\nkey: ' + key2 + '\n' + 'value: ' + screen[key][key2] + '\n');
						}
					 }
        }
				*/
				
				screen.patching.hide();
				//screen.environments.show();
				screen.promos.show();
    }
}

function StartLastDownload()
{
	var repromo=false;
    if( $("#promos").is(":visible") )
    {
    	screen.promos.hide();
    	repromo=true;
    }
    screen.patching.show();
    screen.patching.showProgress();
    patchCompleteCallBack=function()
    {
        FinishedPatchingUI();
        if( repromo )
        {
					screen.patching.hide();
					$("#nextButton, #updateButton, #pauseButton, #resumeButton").hide();
        	screen.promos.show();
        }
    }
    currentDownloadingProductName = lastDownloadingProductName;
    if((pickedEnv.altManifestUrl) && (pickedEnv.altManifestUrl != '') && (pickedEnv.altManifestUrl != 'null'))
    	manifesturl=pickedEnv.altManifestUrl;
    else
	    manifesturl=external_manifest_url;
			setTimeout(SSNStartDownload,1);
}

function SSNStartDownload()
{
    lastByteDate = new Date();
	if(currentDownloadingProductName != 'Unknown')
	{
		lastDownloadingProductName=currentDownloadingProductName;
	    SSN.Patcher.addPackageList(currentDownloadingProductName, "http://"+manifesturl+"/sspatch/"+currentDownloadingProductName+".packagelist", SSN.Launcher.getDirectory());
	    updateDownloadSettings();
	}
	else
		log('ERROR','Not adding Unknown package list');
	
}

function updateDownloadSettings()
{
	if(currentDownloadingProductName != 'Unknown')
	{
	    SSN.Patcher.setPackageListOptions(currentDownloadingProductName, false, !SSN.isPaused);
	}
	else
		log('ERROR','Not updating download settings for Unknown product');
}

function abortCurrentPatch()
{
	if((currentDownloadingProductName==null) 
	||(currentDownloadingProductName=='')
	||(currentDownloadingProductName=='Unknown'))
		return;
	SSN.Patcher.removePackageList(currentDownloadingProductName);
	log('INFO','ABORTED package list '+currentDownloadingProductName);
	currentDownloadingProductName='Unknown';
    manifesturl=external_manifest_url;
}

function patchThePatcher()
{
    currentDownloadingProductName = 'patcher';
    manifesturl=external_manifest_url;
    log('INFO','Starting patcher patch');
    patchCompleteCallBack=function(){ };
    setTimeout(SSNStartDownload,1);
}

